#ifndef NE_STL_INCLUDENE_STL_DELEGATOR_STORAGE_H
#define NE_STL_INCLUDENE_STL_DELEGATOR_STORAGE_H

#include <algorithm>
#include "ne_stl_bind.h"
#include "ne_stl_continuous_container.h"
#include "ne_stl_spinlock.h"
namespace nstd {

/// !thread safe
using unregister_delegate_t = nstd::function<void(void)>;
class _unregister_delegator {
public:
    _unregister_delegator() = default;
    ~_unregister_delegator() { clear(); }
    void clear() {
        nstd::vector<unregister_delegate_t> _temp_callback_list;
        {
            nstd::lock_guard auto_lock_raii(callback_list_lock_);
            _temp_callback_list = callback_list_;
            callback_list_.clear();
        }
        for (auto& iter : _temp_callback_list) {
            if (iter != nullptr) {
                (iter)();
            }
        }
    }
    void add_delegate(const unregister_delegate_t& _delegate) {
        nstd::lock_guard auto_lock_raii(callback_list_lock_);
        callback_list_.append(_delegate);
    }

private:
    nstd::spinlock callback_list_lock_;
    nstd::vector<unregister_delegate_t> callback_list_;
};
using unregister_delegator_t = _unregister_delegator;

template <typename t_delegator>
class _delegator_storage {
public:
    _delegator_storage() {}
    ~_delegator_storage() { clear(); };
    inline void clear() {
        nstd::lock_guard auto_lock(lock_);
        element_list_.clear();
    }
    inline unregister_delegate_t add_delegate(const t_delegator& _delegate, bool exclusive = false) {
        auto _new_delegate = nstd::make_shared<t_delegator>(_delegate);
        size_t _new_delegate_id = (size_t)(_new_delegate.get());
        {
            nstd::lock_guard auto_lock(lock_);
            if (exclusive)
                element_list_.clear();
            element_list_.append(make_pair(_new_delegate_id, _new_delegate));
        }
        return ([this, _weak_flag = weak_flag_.get_weak_flag(), _new_delegate_id]() {
            if (!_weak_flag.expired()) {
                nstd::lock_guard auto_lock(lock_);
                auto it = std::find_if(element_list_.begin(), element_list_.end(), [&](const pair<size_t, nstd::shared_ptr<t_delegator>>& item) {
                    return item.first == _new_delegate_id;
                });
                if (it != element_list_.end())
                    element_list_.erase(it);
            }
        });
    }
    inline operator bool() const {
        nstd::lock_guard auto_lock(lock_);
        return !element_list_.empty();
    }
    inline bool operator==(std::nullptr_t) const { return !operator bool(); }
    inline bool operator!=(std::nullptr_t) const { return operator bool(); }
    template <typename... t_params>
    inline void operator()(t_params... params) {
        nstd::vector<pair<size_t, nstd::shared_ptr<t_delegator>>> _element_list;
        {
            nstd::lock_guard auto_lock(lock_);
            _element_list = element_list_;
        }
        for (auto& it : _element_list) {
            if (it.second != nullptr) {
                (*it.second)(params...);
            }
        }
    }

private:
    mutable nstd::spinlock lock_;
    nstd::vector<pair<size_t, nstd::shared_ptr<t_delegator>>> element_list_;
    nstd::weak_semantics_supporter weak_flag_;
};
template <typename t_delegator>
using delegator_storage_t = _delegator_storage<t_delegator>;

}  // namespace nstd

#endif  // !NE_STL_INCLUDENE_STL_DELEGATOR_STORAGE_H
